package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.aliyun.GreenImageScan;
import com.heima.aliyun.GreenTextScan;
import com.heima.common.exception.CustException;
import com.heima.feigns.AdminFeign;
import com.heima.model.common.constants.message.PublishArticleConstants;
import com.heima.model.common.constants.wemedia.WemediaConstants;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.service.WmNewsAutoScanService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author mrchen
 * @date 2022/1/3 10:31
 */
@Service
@Slf4j
public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService {

    @Autowired
    WmNewsMapper wmNewsMapper;

    @Value("${file.oss.web-site}")
    String webSite;

    @Autowired
    private RabbitTemplate rabbitTemplate;


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void autoScanWmNews(Integer id) {
        log.info("接收到 id 为{} 的自动审核消息   自动审核开始~~");
        // 根据id查询自媒体文章内容
        WmNews wmNews = wmNewsMapper.selectById(id);
        if (wmNews == null) {
            log.error("文章自动审核失败   自媒体文章不存在 文章id:{}",id);
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"文章自动审核失败   自媒体文章不存在 文章id:"+id);
        }
        // 判断文章状态是否为 1   待审核
        if(!WemediaConstants.WM_NEWS_SUMMIT_STATUS.equals(wmNews.getStatus())){
            log.error("文章自动审核失败   自媒体文章不是待审核状态 文章id:{}   文章状态: {}",id,wmNews.getStatus());
            CustException.cust(AppHttpCodeEnum.DATA_NOT_ALLOW,"文章自动审核失败   自媒体文章不是待审核状态 文章id: "+id);
        }
        //  先抽取文章中所有的文本内容 和 图片内容  map ( content : 文本内容   ,   images : 图片集合)  String    List<String>
        Map<String,Object> textAndImageMap =  handleTextAndImages(wmNews);
        //  使用自管理敏感词 进行敏感词审核    2 未通过    有敏感词  false    无 true
        boolean isSensitive = handleSensitive(textAndImageMap.get("content").toString(),wmNews);
        if(!isSensitive) {
            log.error("文章自动审核失败   文章中包含敏感词 文章id:{}",id);
            return;
        }
        //  使用阿里云内容安全进行文本审核   2 未通过   3 带人工审核    违规 false  通过 true
        boolean isTextScan = handleTextScan(textAndImageMap.get("content").toString(),wmNews);
        if(!isTextScan) {
            log.error("文章自动审核失败   文章中包含违规信息 文章id:{}",id);
            return;
        }
        //  使用阿里云内容安全进行图片审核   2 未通过   3 带人工审核
        List<String> images =  (List<String>)textAndImageMap.get("images");
        if(CollectionUtils.isNotEmpty(images)){
            boolean isImageScan = handleImageScan(images,wmNews);
            if(!isImageScan) {
                log.error("文章自动审核失败   文章图片中 包含违规信息 文章id:{}",id);
                return;
            }
        }
        //  将文章的状态修改 为 8   自动审核通过
        updateWmNews(wmNews,WmNews.Status.SUCCESS.getCode(),"审核通过");
        // TODO 发送延迟发布文章消息  触发文章发布上架
        // 1. 获取发布时间
        long publishTime = wmNews.getPublishTime().getTime();
        // 2. 当前时间
        long nowTime = System.currentTimeMillis();
        // 3. 发布时间 - 当前时间 = 距离发布的时间
        long remainTime = publishTime - nowTime;
        // 4. 发消息 --> 设置消息header ==> x-delay 延迟时间
        rabbitTemplate.convertAndSend(PublishArticleConstants.DELAY_DIRECT_EXCHANGE,
                PublishArticleConstants.PUBLISH_ARTICLE_ROUTE_KEY,
                wmNews.getId(),
                new MessagePostProcessor() {
                    // 消息的后置处理器，可以对消息进行自定义设置
                    @Override
                    public Message postProcessMessage(Message message) throws AmqpException {
                        MessageProperties messageProperties = message.getMessageProperties();
                        messageProperties.setHeader("x-delay",remainTime<=0 ? 0 : remainTime);
                        return message;
                    }
                }
        );
        log.info("成功发送  待发布消息 当前时间: {}", LocalDateTime.now().toString());
    }


    @Autowired
    GreenImageScan greenImageScan;

    private boolean handleImageScan(List<String> images, WmNews wmNews) {
        boolean isFlag = true;
        try {
            Map map = greenImageScan.imageUrlScan(images);
            String suggestion = (String)map.get("suggestion");
            switch (suggestion){
                case "block":
                    isFlag = false;
                    updateWmNews(wmNews,WmNews.Status.FAIL.getCode(),"图片中包含违规内容");
                    break;
                case "review":
                    isFlag = false;
                    updateWmNews(wmNews,WmNews.Status.ADMIN_AUTH.getCode(),"图片中有不确定的信息，需要进一步人工审核");
                    break;
                default:
            }
        } catch (Exception e) {
            e.printStackTrace();
            // 将状态修改为 3    带人工审核
            log.error(" 阿里云文本内容安全接口调用失败");
            updateWmNews(wmNews,WmNews.Status.ADMIN_AUTH.getCode(), "阿里云图片内容安全接口调用失败,转为人工审核");
            isFlag = false;
        }
        return isFlag;
    }

    @Autowired
    GreenTextScan greenTextScan;


    private boolean handleTextScan(String content, WmNews wmNews) {
        boolean isFlag = true;
        try {
            Map map = greenTextScan.greenTextScan(content);
            String suggestion = (String)map.get("suggestion");
            switch (suggestion){
                case "block":
                    isFlag = false;
                    // TODO  可以扩展 返回具体为什么违规的信息
                    updateWmNews(wmNews,WmNews.Status.FAIL.getCode(),"文本中包含违规内容");
                    break;
                case "review":
                    isFlag = false;
                    updateWmNews(wmNews,WmNews.Status.ADMIN_AUTH.getCode(),"文本中有不确定的信息，需要进一步人工审核");
                    break;
                default:
            }
        } catch (Exception e) {
            e.printStackTrace();
            // 将状态修改为 3    带人工审核
            log.error(" 阿里云文本内容安全接口调用失败");
            updateWmNews(wmNews,WmNews.Status.ADMIN_AUTH.getCode(), "阿里云文本内容安全接口调用失败,转为人工审核");
            isFlag = false;
        }
        return isFlag;
    }


    @Autowired
    AdminFeign adminFeign;

    /**
     * @param content  文本内容
     * @param wmNews  文章
     * @return
     */
    private boolean handleSensitive(String content, WmNews wmNews) {
        boolean isFlag = true;
        // 1. 查询出敏感词列表
        ResponseResult<List<String>> sensitivesResult = adminFeign.sensitives();
        if (!sensitivesResult.checkCode()) {
            log.error("自动审核失败  原因远程调用敏感词数据失败");
            CustException.cust(AppHttpCodeEnum.REMOTE_SERVER_ERROR,"远程调用查询敏感词失败");
        }
        List<String> sensitives = sensitivesResult.getData();
        if (CollectionUtils.isNotEmpty(sensitives)) {
            // 2. 基于DFA工具类生成数据模型
            SensitiveWordUtil.initMap(sensitives);
            // 3. 基于DFA检测是否包含敏感词
            Map<String, Integer> map = SensitiveWordUtil.matchWords(content);
            // 4. 如果有敏感词词  将wmNews状态修改为2
            if(map!=null && map.size()>0){
                updateWmNews(wmNews, WmNews.Status.FAIL.getCode(), "文本中包含敏感词: " + map);
                isFlag = false;
            }
        }
        return isFlag;
    }

    /**
     * @param wmNews  要修改的文章信息
     * @param status  要修改的状态
     * @param reason   原因
     */
    private void updateWmNews(WmNews wmNews, Short status, String reason) {
        wmNews.setStatus(status);
        wmNews.setReason(reason);
        wmNewsMapper.updateById(wmNews);
    }

    /**
     * 抽取文章中所有的文本内容 和 图片路径集合
     *
     * 标题  +  内容
     *
     * 内容中引用的图片   封面中引用的图片
     *
     * @param wmNews
     * @return
     */
    private Map<String, Object> handleTextAndImages(WmNews wmNews) {
        // 1. 获取文章内容  并转为 List<Map> 集合
        List<Map> contentMapList = JSON.parseArray(wmNews.getContent(), Map.class);
        // 2. 拼接文本内容 [{type:text,value: "  家乡好美"}]  + title   国家伟大       家乡好美_hmtt_国家伟大
        String content = contentMapList.stream()
                // 筛选内容中 所有文本内容
                .filter(m -> "text".equals(m.get("type")))
                // 获取文本类型map中的文本内容
                .map(m -> m.get("value").toString())
                // 将所有文本拼接成一个大的字符串
                .collect(Collectors.joining("_hmtt_"));
        content += "_hmtt_" + wmNews.getTitle();
        // 3. 拼接图片路径集合
        List<String> images = contentMapList.stream()
                // 筛选内容中 所有文本内容
                .filter(m -> "image".equals(m.get("type")))
                // 获取文本类型map中的图片路径(全路径)
                .map(m -> m.get("value").toString())
                // 将所有文本拼接成一个大的字符串
                .collect(Collectors.toList());
        if(StringUtils.isNotBlank(wmNews.getImages())){
            // url1,url2,url3 => 切割 返回数组 ==> stream .map(加前缀) .collect 集合
            List<String> coverUrlList = Arrays.stream(wmNews.getImages().split(","))
                    .map(url -> webSite + url)
                    .collect(Collectors.toList());
            images.addAll(coverUrlList);
        }
        images = images.stream().distinct().collect(Collectors.toList());
        // 4. 存入map并返回
        Map<String,Object> result = new HashMap<>(2);
        result.put("content",content);
        result.put("images",images);
        return result;
    }
}